package com.theta.gateway.infra.routecfg.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.theta.gateway.infra.common.base.PageResult;
import com.theta.gateway.infra.common.constants.SystemConstant;
import com.theta.gateway.infra.common.enums.ValidFlagEnum;
import com.theta.gateway.infra.common.exception.BusinessException;
import com.theta.gateway.infra.common.exception.enums.ErrorEnum;
import com.theta.gateway.infra.common.redis.RedisUtils;
import com.theta.gateway.infra.common.redis.constant.GatewayRedisKey;
import com.theta.gateway.infra.common.utils.BeanCopierUtils;
import com.theta.gateway.infra.common.utils.ConvertUtils;
import com.theta.gateway.infra.common.utils.DateUtils;
import com.theta.gateway.infra.common.utils.StrUtil;
import com.theta.gateway.infra.routecfg.mapper.RouteCfgMapper;
import com.theta.gateway.infra.routecfg.model.converter.RouteDTOConverter;
import com.theta.gateway.infra.routecfg.model.dataobject.request.RouteCfgQueryDTO;
import com.theta.gateway.infra.routecfg.model.dataobject.request.RouteCfgSaveDTO;
import com.theta.gateway.infra.routecfg.model.dataobject.request.RouteCfgStatusChangeDTO;
import com.theta.gateway.infra.routecfg.model.dataobject.response.RouteCfgDO;
import com.theta.gateway.infra.routecfg.model.entity.RouteCfgEntity;
import com.theta.gateway.infra.routecfg.service.RouteCfgRepository;

import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 动态路由配置表，服务表 服务实现类
 * </p>
 *
 * @author theta
 */
@Slf4j
@Service
public class RouteCfgRepositoryImpl implements RouteCfgRepository {

	@Autowired
	private RouteCfgMapper routeCfgMapper;
	@Autowired
	private RedisUtils redisUtils;
	
	/**
	 * 生成路由编号
	 * @return
	 */
	private String genRouteCode() {
		String code=null;
		int index=1;
		while(true) {
			code=StringUtils.join(SystemConstant.ROUTE_CODE_PREFIX,StrUtil.getRandNumber(10));
			boolean isExist=this.validCodeExist(code);
			//编号不存在或循环次数超过30次则种植
			if(!isExist || index>20) {
				break;
			}
			index++;
		}
		return code;
	}
	
	/**
	 * 校验路由编号是否存在
	 * @param code
	 * @return
	 */
	private boolean validCodeExist(String code) {
		int count=this.routeCfgMapper.selectCount(new QueryWrapper<RouteCfgEntity>()
				.eq("route_code", code)
				.eq("del_flg", ValidFlagEnum.NO.getCode()));
		return count>0?true:false;
	}

	@Override
	public void saveRouteCfg(RouteCfgSaveDTO saveDto) {
//		cfgDto.setStatus(StrUtil.isEmpty(request.getStatus())?String.valueOf(ValidFlagEnum.NO.getCode()):request.getStatus());
//		log.info("RouteCfgSaveDTO===>{}",JsonUtil.toJsonString(saveDto));
		RouteCfgEntity cfgEntity=new RouteCfgEntity();
		BeanCopierUtils.copyProperties(saveDto, cfgEntity);
		//新增
		if(saveDto.getId()==null || saveDto.getId()<=0) {
//			saveDto.setCreateBy(createBy);
			cfgEntity.setRouteCode(this.genRouteCode());
			cfgEntity.setDelFlg(String.valueOf(ValidFlagEnum.NO.getCode()));
			saveDto.setCreateTime(DateUtils.getCurrentTimeNormal());
			this.routeCfgMapper.insert(cfgEntity);
		}else {
			RouteCfgEntity cfg=this.routeCfgMapper.selectById(saveDto.getId());
			if(cfg==null) {
				throw new BusinessException(ErrorEnum.ROUTE_CFG_ID_NOT_EXIST);
			}
			if(StrUtil.isEmpty(cfg.getRouteCode())) {
				cfgEntity.setRouteCode(this.genRouteCode());
			}
			this.routeCfgMapper.updateById(cfgEntity);
		}
		//更新缓存
		this.refreshRouteCache(cfgEntity);
		saveDto.setId(cfgEntity.getId());
	}

	/**
	 * 刷新路由缓存信息
	 * @param cfgEntity
	 */
	private void refreshRouteCache(RouteCfgEntity cfgEntity) {
		try {
			RouteCfgDO cfgDo=this.getDetailById(cfgEntity.getId());
//			StringUtils.join(cfgDo.getServiceId(),cfgDo.getId()))
			String cacheKey=GatewayRedisKey.genServiceInfoKey(StringUtils.join(cfgDo.getServiceId(),"#",cfgDo.getId()));
			this.redisUtils.set(cacheKey, cfgDo);
		} catch (Exception e) {
			log.error("refreshRouteCache",e);
		}
	}



	/**
	 * 删除路由缓存
	 * @param cfgEntity
	 */
	private void removeRouteCache(RouteCfgEntity cfgEntity) {
		try {
			String cacheKey=GatewayRedisKey.genServiceInfoKey(StringUtils.join(cfgEntity.getServiceId(),"#",cfgEntity.getId()));
			this.redisUtils.delete(cacheKey);
		} catch (Exception e) {
			log.error("removeRouteCache",e);
		}
	}

	@Override
	public void deleteRouteCfg(RouteCfgDO cfgDo) {
		this.routeCfgMapper.deleteById(cfgDo.getId());
		String cacheKey=GatewayRedisKey.genServiceInfoKey(StringUtils.join(cfgDo.getServiceId(),"#",cfgDo.getId()));
		this.deleteRouteCfgCache(cacheKey);
	}

	@Override
	public void deleteRouteCfgCache(String cacheKey) {
		if(StrUtil.isNotEmpty(cacheKey)){
			this.redisUtils.delete(cacheKey);
		}
	}


	@Override
	public void changeRouteStatus(RouteCfgDO cfgDo,RouteCfgStatusChangeDTO changeDto) {
		RouteCfgEntity cfgEntity= this.routeCfgMapper.selectById(changeDto.getId());
//		if(cfgEntity==null)throw new BusinessException(ErrorEnum.ROUTE_CFG_ID_NOT_EXIST);
		this.routeCfgMapper.update(null, new UpdateWrapper<RouteCfgEntity>()
				.set("status", changeDto.getStatus())
				.eq("id", changeDto.getId()));
		//启用 则更新缓存
		if(changeDto.getStatus().intValue()==ValidFlagEnum.VALID.getCode()){
			cfgDo.setStatus(changeDto.getStatus());
			this.refreshRouteCache(cfgEntity);
		//禁用
		}else{
			this.removeRouteCache(cfgEntity);
		}

	}



	@Override
	public RouteCfgDO getDetailById(Long id) {
		if(id==null)return null;
		RouteCfgEntity cfgDo = this.routeCfgMapper.selectById(id);
		return RouteDTOConverter.convertRouteEntity2DTO(cfgDo);
	}

	private QueryWrapper<RouteCfgEntity> initQueryWrapper(RouteCfgQueryDTO query){
		return new QueryWrapper<RouteCfgEntity>()
				.eq("del_flg", ValidFlagEnum.NO.getCode())
				.eq(StrUtil.isNotEmpty(query.getPath()),"path", query.getPath())
				.eq(StrUtil.isNotEmpty(query.getServiceId()), "service_id", query.getServiceId())
				.eq(StrUtil.isNotEmpty(query.getStatus()), "status", query.getStatus())
				.likeRight(StrUtil.isNotEmpty(query.getPathLike()), "path", query.getPathLike())
				;
	}

	@Override
	public PageResult<RouteCfgDO> getRoutePage(RouteCfgQueryDTO query) {
		QueryWrapper<RouteCfgEntity> queryWrapper=this.initQueryWrapper(query);
		queryWrapper.orderByDesc("id");
		IPage<RouteCfgEntity> ipage= this.routeCfgMapper.selectPage(new Page<>(query.getPage().getCurrent(), query.getPage().getSize()), queryWrapper);
		PageResult<RouteCfgDO> result=new PageResult<RouteCfgDO>(ipage.getCurrent(), ipage.getSize(), ipage.getTotal());
		List<RouteCfgDO> records=Lists.newArrayList();
		ipage.getRecords().stream().forEach(r->{
			records.add(RouteDTOConverter.convertRouteEntity2DTO(r));
		});
		result.setRecords(records);
		return result;
	}



	@Override
	public List<RouteCfgDO> getAllRouteCfgs() {
		List<RouteCfgDO> routes=Lists.newArrayList();
		List<RouteCfgEntity> cfgList= this.routeCfgMapper.selectList(new QueryWrapper<RouteCfgEntity>()
				.eq("del_flg", ValidFlagEnum.NO.getCode())
				.eq("status",ValidFlagEnum.ENABLED.getCode())
				);
		if(cfgList!=null) {
			routes=cfgList.stream().map(cfg->RouteDTOConverter.convertRouteEntity2DTO(cfg)).collect(Collectors.toList());
		}
		return routes;
	}



	@Override
	public List<RouteCfgDO> getRouteCfgs(RouteCfgQueryDTO query) {
		QueryWrapper<RouteCfgEntity> queryWrapper=this.initQueryWrapper(query);
		List<RouteCfgEntity> list = this.routeCfgMapper.selectList(queryWrapper);
		List<RouteCfgDO> routes=ConvertUtils.sourceToTarget(list, RouteCfgDO.class);
		return routes;
	}



	@Override
	public List<RouteCfgDO> getServiceRoutes(String serviceId) {
		RouteCfgQueryDTO query=new RouteCfgQueryDTO();
		query.setServiceId(serviceId);
		//1-启用
		query.setStatus(ValidFlagEnum.ENABLED.getCode());
		return this.getRouteCfgs(query);
	}



	@Override
	public RouteCfgDO getCacheServiceRoute(String serviceKey) {
		String cacheKey=GatewayRedisKey.genServiceInfoKey(serviceKey);
		RouteCfgDO cfgDo=(RouteCfgDO)this.redisUtils.get(cacheKey);
		if(cfgDo==null) {
//			serviceId=serviceId.contains("#")?serviceId.split("#")[0]:serviceId;
//			List<RouteCfgDO> routes=this.getServiceRoutes(serviceId);
//			cfgDo=CollectionUtils.isEmpty(routes)?null:routes.get(0);
			Long routeId=StrUtil.obj2Long(serviceKey.split("#")[1]);
			//根据ID查询路由信息
			cfgDo=this.getDetailById(routeId);
			if(cfgDo!=null) {
				this.redisUtils.set(cacheKey, cfgDo,GatewayRedisKey.GATEWAY_KEY_SERVICE_INFO_EXPIRE_TIME);
			}
		}
		return cfgDo;
	}
	
	

}
